`include "global_def.h"

module Writeback(
  I_CLOCK,
  I_LOCK,
  I_Opcode,
  I_ALUOut,
  I_MemOut,
  I_DestRegIdx,
  I_FetchStall,
  I_DepStall,
  I_VectorALUOut, //New Inputs
  I_VectorDestRegIdx, //End New Inputs
  I_RAST_STALL,
  O_WriteBackEnable,
  O_WriteBackRegIdx,
  O_WriteBackData,
  O_VectorWriteBackEnable, //New Outputs
  O_VectorWriteBackRegIdx, //New Outputs
  O_VectorWriteBackData, //New Outputs
  O_VectorALUOut, //New Output
  O_Opcode, //New Output
  O_LOCK //New Output
);

/////////////////////////////////////////
// IN/OUT DEFINITION GOES HERE
/////////////////////////////////////////
//
// Inputs from the memory stage
input I_CLOCK;
input I_LOCK;
input I_FetchStall;
input [`OPCODE_WIDTH-1:0] I_Opcode;
input [3:0] I_DestRegIdx;
input [`REG_WIDTH-1:0] I_ALUOut;
input [`REG_WIDTH-1:0] I_MemOut;
input I_DepStall;
input [`VREG_WIDTH-1:0] I_VectorALUOut;
input [5:0] I_VectorDestRegIdx;
input I_RAST_STALL; //Stall from Rasterisation

// Outputs to the decode stage
output O_WriteBackEnable;
output [3:0] O_WriteBackRegIdx;
output [`REG_WIDTH-1:0] O_WriteBackData;

// Outputs to the decode stage
output O_VectorWriteBackEnable;
output [5:0] O_VectorWriteBackRegIdx;
output [`VREG_WIDTH-1:0] O_VectorWriteBackData;

//Outputs to the Vertex stage
output reg [`VREG_WIDTH-1:0] O_VectorALUOut;
output reg [`OPCODE_WIDTH-1:0] O_Opcode;
output reg O_LOCK;

/////////////////////////////////////////
// ## Note ##
// - Assign output signals depending on opcode.
// - A few examples are provided.
/////////////////////////////////////////
assign O_WriteBackEnable = 
  ((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ? 
    ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_ADD_D) ? (1'b1) :
       (I_Opcode == `OP_ADDI_D) ? (1'b1) :
		 (I_Opcode == `OP_AND_D) ? (1'b1) :
		 (I_Opcode == `OP_ANDI_D) ? (1'b1) :
		 (I_Opcode == `OP_MOV) ? (1'b1) :
       (I_Opcode == `OP_MOVI_D) ? (1'b1) :
		 (I_Opcode == `OP_LDW) ? (1'b1) :
       /////////////////////////////////////////////
       // TODO: Complete other instructions
       /////////////////////////////////////////////
		 (I_Opcode == `OP_STW) ? (1'b0) :
       (I_Opcode == `OP_JSR) ? (1'b0) :
		 (I_Opcode == `OP_JSRR) ? (1'b0) : 
		 (I_Opcode == `OP_JMP) ? (1'b0) :
		 (I_Opcode == `OP_RET) ? (1'b0) :
		 (I_Opcode == `OP_BRN) ? (1'b0) :
		 (I_Opcode == `OP_BRZ) ? (1'b0) :
		 (I_Opcode == `OP_BRP) ? (1'b0) :
		 (I_Opcode == `OP_BRNZ) ? (1'b0) :
		 (I_Opcode == `OP_BRNP) ? (1'b0) :
		 (I_Opcode == `OP_BRZP) ? (1'b0) :
		 (I_Opcode == `OP_BRNZP) ? (1'b0) :
       (1'b0)
      ) : (1'b0)
    ) : (1'b0);
	 
assign O_VectorWriteBackEnable = 
  ((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ?
    ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_VADD) ? (1'b1) :
		 (I_Opcode == `OP_VMOV) ? (1'b1) :
       (I_Opcode == `OP_VMOVI) ? (1'b1) :
		 (I_Opcode == `OP_VCOMPMOV) ? (1'b1) :
		 (I_Opcode == `OP_VCOMPMOVI) ? (1'b1) :
       /////////////////////////////////////////////
       // TODO: Complete other instructions
       /////////////////////////////////////////////
       (1'b0)
      ) : (1'b0)
    ) : (1'b0);

assign O_WriteBackRegIdx = 
  ((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ?
    ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_ADD_D) ? (I_DestRegIdx) :
       (I_Opcode == `OP_ADDI_D) ? (I_DestRegIdx) :
		 (I_Opcode == `OP_AND_D) ? (I_DestRegIdx) :
		 (I_Opcode == `OP_ANDI_D) ? (I_DestRegIdx) :
		 (I_Opcode == `OP_MOV) ? (I_DestRegIdx) :
       (I_Opcode == `OP_MOVI_D) ? (I_DestRegIdx) :
		 (I_Opcode == `OP_LDW) ? (I_DestRegIdx) :
       /////////////////////////////////////////////
       // TODO: Complete other instructions
       /////////////////////////////////////////////
       (I_Opcode == `OP_LDB) ? (I_DestRegIdx) :
       (4'h0)
      ) : (1'b0)
    ) : (1'b0);
	 
assign O_VectorWriteBackRegIdx = 
  ((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ?
    ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_VADD) ? (I_VectorDestRegIdx) :
		 (I_Opcode == `OP_VMOV) ? (I_VectorDestRegIdx) :
       (I_Opcode == `OP_VMOVI) ? (I_VectorDestRegIdx) :
		 (I_Opcode == `OP_VCOMPMOV) ? (I_VectorDestRegIdx) :
		 (I_Opcode == `OP_VCOMPMOVI) ? (I_VectorDestRegIdx) :
       /////////////////////////////////////////////
       // TODO: Complete other instructions
       /////////////////////////////////////////////
       (6'h0)
      ) : (1'b0)
    ) : (1'b0);

assign O_WriteBackData = 
  ((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ?
    ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_ADD_D) ? (I_ALUOut) :
       (I_Opcode == `OP_ADDI_D) ? (I_ALUOut) :
		 (I_Opcode == `OP_AND_D) ? (I_ALUOut) :
		 (I_Opcode == `OP_ANDI_D) ? (I_ALUOut) :
		 (I_Opcode == `OP_MOV) ? (I_ALUOut) :
       (I_Opcode == `OP_MOVI_D) ? (I_ALUOut) :
       /////////////////////////////////////////////
       // TODO: Complete other instructions
       /////////////////////////////////////////////
		 (I_Opcode == `OP_LDW) ? (I_MemOut) :
       (I_Opcode == `OP_LDB) ? (I_MemOut) : 
       (16'h0000)
      ) : (1'b0)
    ) : (1'b0);
	 
assign O_VectorWriteBackData = I_VectorALUOut;

always @(negedge I_CLOCK)
begin
	if (!I_RAST_STALL) begin
		if(I_LOCK) begin
		  O_LOCK <= I_LOCK;
		  O_Opcode <= I_Opcode;
		  O_VectorALUOut <= I_VectorALUOut;
	  end
	end // if (!I_RAST_STALL)
end // always @(negedge I_CLOCK)

endmodule // module Writeback
